package downloader

import (
	"context"
	"gitee.com/Luna-CY/hui-hui/internal/logger"
	"gitee.com/Luna-CY/hui-hui/internal/toolbox/resources"
	"gitee.com/Luna-CY/hui-hui/internal/util/downloader"
	"io"
	"os"
	"sync"
	"time"
)

type TaskState int

const (
	TaskStateDownloading = TaskState(iota) + 1 // 下载中
	TaskStateError                             // 下载失败
	TaskStateComplete                          // 下载完成
	TaskStateCanceled                          // 已取消
)

type TaskTarget int

const (
	TaskTargetResources = TaskTarget(iota) + 1 // 资源管理器
	TaskTargetTemporary                        // 临时文件
)

type Task struct {
	ctx    context.Context    // 上下文
	cancel context.CancelFunc // 取消方法

	Id      string    // 任务ID
	State   TaskState // 状态
	Message string    // 状态的文字描述

	StartTime        time.Time     // 开始时间
	EndTime          time.Time     // 结束时间
	Filename         string        // 文件名
	FileSize         int64         // 文件大小，单位字节
	DownloadSize     int64         // 已下载的字节数
	RemoteAddress    string        // 远程文件地址
	LocalFilepath    string        // 本地文件路径，应用退出时需要删除此路径
	LocalFileRemoved bool          // 本地缓存是否已移除
	Target           TaskTarget    // 保存目标
	SavePath         []string      // 保存到资源管理器的路径
	Timeout          time.Duration // 任务文件保留时长
}

var once sync.Once
var instance *Downloader

func Single(resources *resources.Resources) *Downloader {
	once.Do(func() {
		instance = &Downloader{tasks: make(map[string]*Task), resources: resources}
	})

	return instance
}

// Downloader 下载器
type Downloader struct {
	resources *resources.Resources

	mutex sync.RWMutex     // 读写锁
	tasks map[string]*Task // 任务列表，内存保存，不支持持久化
	quit  bool             // 是否已退出
}

// started 封装任务启动后的处理逻辑
func (cls *Downloader) started(task *Task) {
	defer task.cancel()

	var _, file, err = downloader.DownloadToTempFile(task.ctx, task.RemoteAddress, func(total int64, current int64) {
		task.DownloadSize = current
	})

	task.EndTime = time.Now()

	// 下载失败
	if nil != err && context.Canceled != err {
		logger.GetLogger().Sugar().Errorf("下载器: 下载文件失败: %s", err)

		task.State = TaskStateError
		task.Message = err.Error()
		task.EndTime = time.Now()

		return
	}

	// 主动取消文件下载
	if context.Canceled == err {
		logger.GetLogger().Sugar().Errorf("下载器: 下载已取消: %s", task.RemoteAddress)

		task.State = TaskStateCanceled
		task.Message = "已取消"

		return
	}

	var remove bool
	// 关闭文件句柄
	defer func() {
		if err := file.Close(); nil != err {
			logger.GetLogger().Sugar().Errorf("下载器: 关闭临时文件失败: %s", err)
		}

		if remove {
			if err := os.RemoveAll(file.Name()); nil != err {
				logger.GetLogger().Sugar().Errorf("下载器: 移除临时文件失败: %s", err)
			}
		}
	}()

	// 移动到资源管理
	if TaskTargetResources == task.Target {
		remove = true
		if _, err := file.Seek(0, io.SeekStart); nil != err {
			logger.GetLogger().Sugar().Errorf("下载器: 移动到资源管理器失败: %s", err)

			task.State = TaskStateError
			task.Message = err.Error()
			task.EndTime = time.Now()

			return
		}

		if err := cls.resources.MakeDir(task.SavePath); nil != err {
			logger.GetLogger().Sugar().Errorf("下载器: 移动到资源管理器失败: %s", err)

			task.State = TaskStateError
			task.Message = err.Error()
			task.EndTime = time.Now()

			return
		}

		if err := cls.resources.SaveFile(append(task.SavePath, task.Filename), file); nil != err {
			logger.GetLogger().Sugar().Errorf("下载器: 移动到资源管理器失败: %s", err)

			task.State = TaskStateError
			task.Message = err.Error()
			task.EndTime = time.Now()

			return
		}
	}

	logger.GetLogger().Sugar().Infof("下载器: 下载完成: %s", task.RemoteAddress)

	task.LocalFilepath = file.Name()
	task.State = TaskStateComplete
	task.Message = "下载完成"
}
